import React from 'react';
import ReactDOM from 'react-dom';

import { fromJS } from 'immutable';
import styles from './index.less';
import {
  get_global_offsets,
  get_scale,
  set_scale,
  set_latest_data,
  get_latest_data,
  get_latest_selected,
  set_latest_selected,
  img_stop_propagation_events,
  get_global_offsets_flat,
  set_global_offsets_flat,
  get_node_offset,
  arr_equals,
  useState,
  dis2d,
  gen_id,
  get_global_offset,
  sleep,
  get_node_size,
} from './utils';
import {
  INPUT_ID,
  MIN_ATTACH_RANGE,
  MIN_DRAG_RANGE,
  Data,
  Offset,
  Size,
  MIN_DIS_SIB,
  DIS_PARENT_CHILDREN,
  OffsetChangedType,
} from './common';
import { svgToDataURL, compute_text_size, upload, retrieve } from './utils';
import { MindNode } from './node';

const CanvasEditor = React.lazy(
  () => import(/* webpackChunkName: "canvas-editor" */ './canvas-editor'),
);
const latestMouseDown: Offset = { x: 0, y: 0 };
const latestMouseMove: Offset = { x: 0, y: 0 };

let first_update = true;
const workspaceLatestMouseDown: Offset = { x: 0, y: 0 };
const workspaceLatestMouseMove: Offset = { x: 0, y: 0 };
let initialOffset: Offset = { x: 0, y: 0 };
let workspace_dragging = false;
let workspace_dragging_ready = false;
let workspace_is_dragging = false;

let is_dragging = false;
let dragging_ready = false;
let latest_path: string[] = [];
let latest_clue = undefined;
let latest_dragging_data = undefined;

enum ClueType {
  insert,
  appendChild,
}

async function update_global_offsets() {
  const candidates: { path: any[]; offset: Offset }[] = [];
  const g = get_global_offsets();
  const search = (d, path, size, parent_offset = { x: 0, y: 0 }) => {
    if (!d.offset || !d.size) {
      console.log('#');
      return;
    }
    const new_offset = {
      x: d.offset.x + d.size.width + parent_offset.x,
      y: d.offset.y + d.size.height + parent_offset.y,
    };
    d.global_offset = new_offset;
    candidates.push({
      path,
      offset: new_offset,
    });
    d.children
      ? d.children.forEach((i, index) =>
          search(i, [...path, 'children', index], d.size, new_offset),
        )
      : null;
  };
  set_global_offsets_flat(candidates);
  search(g, [], g.size);
}

export const MindEditor = (props) => {
  const { slideWidth, setOffset, offset } = props;
  const [canvasEditor, setCanvasEditor] = useState({
    show: false,
    img: '',
    cb: () => {},
  });
  const { setData, data, projectId } = props;
  const [scale, setScale] = useState(get_scale());
  const [dragging_data, setDraggingData] = useState(undefined);
  const [help_clicked, set_help_clicked] = useState(false);
  const [dragging_offset, setDraggingOffset] = useState({ x: 0, y: 0 });
  const [foldInsert, setFoldInsert] = useState(true);
  const [sharingDialog, setSharingDialog] = useState({
    show: false,
    current: 0,
    total: 0,
  });

  const [selected_img, setSelectedImg] = useState({
    index: 0,
    show: false,
    independent: false,
    size: {
      width: 0,
      height: 0,
    },
  });
  const [clue, setClue] = useState({
    show: false,
  });
  latest_clue = clue;
  latest_dragging_data = dragging_data;
  const [selected, setSelected] = useState(undefined);

  if (first_update) {
    first_update = false;
  }

  React.useEffect(() => {
    set_latest_selected(selected);
  }, [selected]);

  React.useEffect(() => {
    set_latest_data(data);
    update_global_offsets();
  }, [data, offset]);

  React.useEffect(() => {
    update_global_offsets();
  }, [scale]);

  function getGlobalOffsetByPath(path: any[]) {
    let g = get_global_offsets();
    for (let i = 0; i < path.length; ++i) {
      g = g[path[i]];
    }
    return g;
  }

  function unselect() {
    setFoldInsert(true);
    if (!get_latest_data()) {
      return;
    }
    const id = get_latest_data().getIn([...latest_path, 'id']);
    const item = document.getElementById(id);
    item && item.removeAttribute('data-active');
    setSelected(undefined);
    if (selected_img.show) {
      setSelectedImg({
        ...selected_img,
        show: false,
      });
    }
  }
  React.useEffect(() => {
    unselect();
  }, [projectId]);

  function selectByPath(path: any[], new_data?) {
    unselect();
    latest_path = path;
    setTimeout(() => {
      const d = new_data || get_latest_data();
      const g = getGlobalOffsetByPath(path);
      setSelected({
        ignore: !!d.getIn([...path, 'ignore']),
        important: !!d.getIn([...path, 'important']),
        merge: !!d.getIn([...path, 'merge']),
        offset: g.global_offset,
        text: d.getIn([...path, 'name']),
        size: g.size,
      });
      const id = (new_data || get_latest_data()).getIn([...path, 'id']);
      document.getElementById(id).setAttribute('data-active', 'true');
      (document.getElementById(INPUT_ID) as any).focus();
      const ws = document.getElementById('workspace');
      if (ws && (ws.scrollTop !== 0 || ws.scrollLeft !== 0)) {
        ws.scrollTop = 0;
        ws.scrollLeft = 0;
      }
    }, 1);
  }

  function appendChild(
    d = {
      id: gen_id(),
      name: '',
      imgs: [],
      children: [],
    },
  ) {
    unselect();
    const new_path = [...latest_path, 'children'];
    const new_data = get_latest_data().updateIn(new_path, (list) =>
      list.push(fromJS(d)),
    );
    setData(new_data);
    selectByPath([...new_path, new_data.getIn(new_path).size - 1], new_data);
  }

  function insert(p = latest_path) {
    unselect();
    const new_path = p.slice(0, p.length - 1);
    const index = p[p.length - 1] + 1;
    const new_data = get_latest_data().updateIn(new_path, (list) =>
      list.insert(
        index,
        fromJS({
          id: gen_id(),
          name: '',
          imgs: [],
          children: [],
        }),
      ),
    );
    setData(new_data);
    selectByPath([...new_path, index], new_data);
  }

  function workspace_on_mouse_down(workspace_offset) {
    console.log('w d');
    if (get_latest_selected()) {
      unselect();
    }
    workspace_dragging_ready = true;
    workspaceLatestMouseDown.x = workspace_offset.x;
    workspaceLatestMouseDown.y = workspace_offset.y;
    initialOffset = {
      x: offset.x,
      y: offset.y,
    };
  }

  function workspace_on_mouse_move(offset) {
    const data = get_latest_data();
    if (workspace_dragging_ready) {
      console.log('w m');
      workspaceLatestMouseMove.x = offset.x;
      workspaceLatestMouseMove.y = offset.y;
      if (
        !workspace_is_dragging &&
        dis2d(workspaceLatestMouseMove, workspaceLatestMouseDown) >
          MIN_DRAG_RANGE
      ) {
        workspace_is_dragging = true;
      }
      if (workspace_is_dragging) {
        setOffset({
          x:
            initialOffset.x +
            workspaceLatestMouseMove.x -
            workspaceLatestMouseDown.x,
          y:
            initialOffset.y +
            workspaceLatestMouseMove.y -
            workspaceLatestMouseDown.y,
        });
      }
    } else if (dragging_ready) {
      console.log('n m');
      latestMouseMove.x = offset.x;
      latestMouseMove.y = offset.y;
      if (
        !is_dragging &&
        dis2d(latestMouseMove, latestMouseDown) > MIN_DRAG_RANGE
      ) {
        is_dragging = true;
        setDraggingData(data.getIn(latest_path));
        setData(data.deleteIn(latest_path));
      }
      if (is_dragging) {
        setDraggingOffset({
          x: latestMouseMove.x,
          y: latestMouseMove.y,
        });
        const candidate = get_global_offsets_flat().filter(
          (i) => dis2d(i.offset, latestMouseMove) < MIN_ATTACH_RANGE,
        )[0];
        if (candidate) {
          setClue({
            offset: candidate.offset,
            path: candidate.path,
            show: true,
            type:
              candidate.path.length > 0 &&
              candidate.offset.x > latestMouseMove.x
                ? ClueType.insert
                : ClueType.appendChild,
          });
        } else {
          setClue({ show: false });
        }
      }
    }
  }

  function node_on_mouse_down(offset, path) {
    console.log('n d');
    if (get_latest_selected() || !arr_equals(latest_path, path)) {
      unselect();
    }
    latest_path = path;
    latestMouseDown.x = offset.x;
    latestMouseDown.y = offset.y;
    if (path.length) {
      dragging_ready = true;
    }
  }

  const mouse_middleware = (e, f, extra?) => {
    const offset = {
      x:
        e.touches && e.touches[0]
          ? e.touches[0].pageX
          : e.changedTouches && e.changedTouches[0]
          ? e.changedTouches[0].pageX
          : e.pageX,
      y:
        e.touches && e.touches[0]
          ? e.touches[0].pageY
          : e.changedTouches && e.changedTouches[0]
          ? e.changedTouches[0].pageY
          : e.pageY,
    };
    f(offset, extra);
  };

  function workspace_on_mouse_up(offset) {
    dragging_ready = false;
    workspace_dragging_ready = false;
    workspace_is_dragging = false;
    const data = get_latest_data();
    const clue = latest_clue;
    const dragging_data = latest_dragging_data;
    if (is_dragging) {
      if (clue.show) {
        setClue({ show: false });
        const name = dragging_data.get('name');
        const new_path: string[] =
          clue.type === ClueType.appendChild
            ? [...clue.path, 'children']
            : clue.path.slice(0, clue.path.length - 1);
        clue.type === ClueType.appendChild
          ? setData(data.updateIn(new_path, (list) => list.push(dragging_data)))
          : setData(
              data.updateIn(new_path, (list) =>
                list.insert(clue.path[clue.path.length - 1] + 1, dragging_data),
              ),
            );
      } else {
        // reset
        const new_data = data.updateIn(
          latest_path.slice(0, latest_path.length - 1),
          (list) =>
            list.insert(latest_path[latest_path.length - 1], dragging_data),
        );

        setData(new_data);
      }
      setDraggingData(undefined);
      is_dragging = false;
      console.log('n u');
    } else {
      console.log('w u');
    }
  }

  function addImage(blob) {
    if (blob) {
      const reader = new FileReader();
      reader.readAsDataURL(blob);
      reader.onload = (event) => {
        const base64 = event.target.result;
        const img = new Image();
        img.src = base64 as string;
        img.onload = () =>
          setData(
            data.updateIn([...latest_path, 'imgs'], (list) =>
              list.push(
                fromJS({
                  id: gen_id(),
                  src: base64,
                  size: {
                    width: img.width,
                    height: img.height,
                  },
                }),
              ),
            ),
          );
      };
    }
  }

  return (
    <div
      className={styles.workspace}
      id="workspace"
      onPaste={(e) => {
        const items = e.clipboardData.items;
        const item = items[items.length - 1];
        if (item.kind === 'file' && item.type.startsWith('image')) {
          const blob = item.getAsFile();
          addImage(blob);
        }
        return true;
      }}
      onWheel={(e: React.WheelEvent<HTMLDivElement>) => {
        if (get_latest_selected()) {
          unselect();
        }
        e.stopPropagation();
        e.preventDefault();
        const r = 0.3;
        setOffset({
          x: offset.x - e.deltaX * r,
          y: offset.y - e.deltaY * r,
        });
      }}
      onMouseUp={(e) => mouse_middleware(e, workspace_on_mouse_up)}
      onMouseMove={(e: React.MouseEvent<HTMLDivElement>) =>
        mouse_middleware(e, workspace_on_mouse_move)
      }
      onMouseDown={(e) => mouse_middleware(e, workspace_on_mouse_down)}
      onTouchMove={(e) => mouse_middleware(e, workspace_on_mouse_move)}
      onTouchStart={(e) => mouse_middleware(e, workspace_on_mouse_down)}
      onTouchEnd={(e) => mouse_middleware(e, workspace_on_mouse_up)}
      style={{
        right: slideWidth,
        width: `calc(100% - ${slideWidth}px)`,
      }}
    >
      {clue.show ? (
        <span
          className={
            clue.type === ClueType.appendChild
              ? styles.appendChild
              : styles.insert
          }
          style={{
            left: clue.offset.x,
            top: clue.offset.y,
          }}
        />
      ) : null}
      <MindNode
        scale={scale}
        comment_on_click={(path) => {
          const old = data.getIn([...path, 'comment']) || '';
          const comment = prompt('添加备注', old);
          if (typeof comment === 'string') {
            setData(data.setIn([...path, 'comment'], comment));
          }
          unselect();
        }}
        img_on_click={(path, index, e) => {
          if (!get_latest_selected()) {
            return;
          }
          setSelectedImg({
            index,
            show: true,
            offset: get_global_offset(e.target),
            independent: !!data.getIn([...path, 'imgs', index, 'independent']),
            size: {
              width: e.target.offsetWidth,
              height: e.target.offsetHeight,
            },
          });
        }}
        notify_global_offset_changed={(
          type: OffsetChangedType,
          path,
          new_offset,
          new_size,
        ) => {
          let o = get_global_offsets();
          if (type === OffsetChangedType.mount) {
            const x = {
              offset: new_offset,
              size: new_size,
            };
            if (path.length) {
              let parent = o;
              for (let i = 0; i < path.length; ++i) {
                parent = o;
                if (o[path[i]] === undefined) {
                  if (path[i] === 'children') {
                    o[path[i]] = [];
                  } else {
                    o[path[i]] = { children: [] };
                  }
                }
                o = o[path[i]];
              }
              if (!o.offset) {
                Object.assign(o, {
                  offset: new_offset,
                  size: new_size,
                });
              } else {
                (parent as any).splice(path[path.length - 1], 0, {
                  ...x,
                  children: [],
                });
              }
            } else {
              Object.assign(o, x);
              if (!o.children) {
                o.children = [];
              }
              update_global_offsets();
            }
          } else if (type === OffsetChangedType.change) {
            for (let i = 0; i < path.length; ++i) {
              if (o[path[i]] === undefined) {
                o[path[i]] = typeof path[i] === 'number' ? {} : [];
              }
              o = o[path[i]];
            }
            const needs_update = !o.size || o.size.height !== new_size.height;
            o.offset = new_offset;
            o.size = new_size;
          } else if (type === OffsetChangedType.remove) {
            for (let i = 0; i < path.length - 1; ++i) {
              if (o[path[i]] === undefined) {
                return;
              }
              o = o[path[i]];
            }
            (o as any).splice(path[path.length - 1], 1);
          }
        }}
        path={[]}
        data={data}
        offset={offset}
        is_root
        events={{
          onClick: (path, e) => {
            e.stopPropagation();
            if (!get_latest_selected()) {
              selectByPath(path);
            } else {
              unselect();
            }
          },
          onMouseDown: (path, e) => {
            e.stopPropagation();
            mouse_middleware(e, node_on_mouse_down, path);
          },
          onTouchStart: (path, e) => {
            e.stopPropagation();
            mouse_middleware(e, node_on_mouse_down, path);
          },
          onTouchMove: (path, e) => {
            e.stopPropagation();
            mouse_middleware(e, workspace_on_mouse_move, path);
          },
          onTouchEnd: (path, e) => {
            e.stopPropagation();
            mouse_middleware(e, workspace_on_mouse_up, path);
          },
        }}
      />
      {dragging_data ? (
        <MindNode
          scale={scale}
          img_on_click={() => {}}
          comment_on_click={() => {}}
          notify_global_offset_changed={() => {}}
          path={[]}
          data={dragging_data}
          offset={dragging_offset}
          is_root
          events={{}}
        />
      ) : null}
      {selected ? (
        <div
          className={styles.selected}
          onMouseDown={(e) => e.stopPropagation()}
          onTouchStart={(e) => e.stopPropagation()}
          style={{
            left: selected.offset.x - selected.size.width,
            top: selected.offset.y - selected.size.height,
            width: selected.size.width,
            height: selected.size.height,
          }}
        >
          <div className={styles.buttons}>
            <input
              id="btn_important"
              type="checkbox"
              className={styles.important}
              checked={selected.important}
              onChange={(e) => {
                setData(
                  data.setIn([...latest_path, 'important'], e.target.checked),
                );
                setSelected({
                  ...selected,
                  important: e.target.checked,
                });
              }}
            />
            <input
              id="btn_merge"
              type="checkbox"
              className={styles.merge}
              checked={selected.merge}
              onChange={(e) => {
                setData(
                  data.setIn([...latest_path, 'merge'], e.target.checked),
                );
                setSelected({
                  ...selected,
                  merge: e.target.checked,
                });
              }}
            />
            <div
              id="btn_share"
              className={styles.share}
              onClick={async (e) => {
                setSharingDialog({
                  show: true,
                  current: 0,
                  total: '?',
                  hash: '?',
                });
                upload(
                  get_latest_data().getIn(latest_path),
                  (current, total, hash) => {
                    console.log(current, total);
                    setSharingDialog({
                      show: true,
                      current,
                      total,
                      hash,
                    });
                  },
                );
              }}
            >
              共享节点
            </div>
            <div
              className={styles.btnInsertAny}
              onClick={() => {
                setFoldInsert(!foldInsert);
              }}
            >
              <div>插入</div>
              {foldInsert ? null : (
                <div className={styles.insertType}>
                  <div
                    id="btn_insert_node"
                    className={styles.btnInsert}
                    onClick={() => {
                      insert();
                    }}
                  >
                    下方插入
                  </div>
                  <div
                    id="btn_append_node"
                    className={styles.btnAppendChild}
                    onClick={() => {
                      appendChild();
                    }}
                  >
                    右方插入
                  </div>
                  <div
                    id="btn_retrieve_node"
                    className={styles.btnRetrieveNode}
                    onClick={async () => {
                      const addr = prompt('输入共享节点地址');
                      if (!addr) {
                        return;
                      }
                      props.setLoading(true);
                      const d = await retrieve(addr);
                      if (d) {
                        appendChild(d);
                      } else {
                        alert('地址错误');
                      }
                      props.setLoading(false);
                    }}
                  >
                    插入共享节点
                  </div>
                  <div
                    id="btn_add_image"
                    className={styles.btnAddImg}
                    onClick={(e) => e.stopPropagation()}
                  >
                    添加图片
                    <input
                      type="file"
                      name=""
                      accept="image/*"
                      className={styles.addImgInput}
                      onInput={(e) => {
                        addImage((e as any).target.files[0]);
                        (e as any).target.value = null;
                      }}
                    />
                  </div>
                  <div
                    id="btn_drawing"
                    className={styles.btnAddDrawing}
                    onClick={() => {
                      setCanvasEditor({
                        show: true,
                        cb: (t, size) => {
                          setData(
                            get_latest_data().updateIn(
                              [...latest_path, 'imgs'],
                              (list) =>
                                list.push(
                                  fromJS({
                                    id: gen_id(),
                                    src: svgToDataURL(t),
                                    size: {
                                      width: size.width,
                                      height: size.height,
                                    },
                                  }),
                                ),
                            ),
                          );
                          setCanvasEditor({ show: false });
                        },
                      });
                    }}
                  >
                    添加绘图
                  </div>
                  <div
                    className={styles.btnAddComment}
                    onClick={() => {
                      const old = data.getIn([...latest_path, 'comment']) || '';
                      const comment = prompt('添加备注', old);
                      if (typeof comment === 'string') {
                        setData(
                          data.setIn([...latest_path, 'comment'], comment),
                        );
                      }
                      unselect();
                    }}
                  >
                    添加备注
                  </div>
                </div>
              )}
            </div>
            <div
              id="btn_remove_node"
              className={styles.btnRemove}
              onClick={() => {
                if (latest_path.length) {
                  setData(data.deleteIn(latest_path));
                }
                unselect();
              }}
            >
              删除
            </div>
          </div>
          <textarea
            className={styles.nameInput}
            value={selected.text}
            onKeyDown={(e) => {
              const keyToAction = {
                Enter: () => {
                  e.preventDefault();
                  e.stopPropagation();
                  if (latest_path.length) {
                    insert(latest_path);
                  } else {
                    unselect();
                  }
                },
                Tab: () => {
                  e.preventDefault();
                  appendChild();
                },
                ArrowLeft: () => {
                  // selectByPath(latest_path.slice(0, latest_path.length - 2));
                },
                ArrowRight: () => {
                  // const children = data.getIn([...latest_path, 'children']);
                  // if (children.size) {
                  //   selectByPath([...latest_path, 'children', 0]);
                  // }
                },
                ArrowUp: () => {
                  if (latest_path.length) {
                    selectByPath([
                      ...latest_path.slice(0, latest_path.length - 1),
                      Math.max(
                        0,
                        (latest_path[latest_path.length - 1] as any) - 1,
                      ),
                    ]);
                  }
                },
                ArrowDown: () => {
                  if (latest_path.length) {
                    const children = data.getIn(
                      latest_path.slice(0, latest_path.length - 1),
                    );
                    selectByPath([
                      ...latest_path.slice(0, latest_path.length - 1),
                      Math.min(
                        children.size - 1,
                        (latest_path[latest_path.length - 1] as any) + 1,
                      ),
                    ]);
                  }
                },
              };
              if (keyToAction[e.key]) {
                keyToAction[e.key]();
              }
            }}
            onInput={(e) => {
              e.preventDefault();
              let g = get_global_offsets();
              for (let i = 0; i < latest_path.length; ++i) {
                g = g[latest_path[i]];
              }

              const node_text_size = compute_text_size(e.currentTarget.value);
              e.currentTarget.parentElement.style.width =
                node_text_size.width + 'px';
              e.currentTarget.parentElement.style.height =
                node_text_size.height + 'px';
              setSelected({
                ...selected,
                offset: g.global_offset,
                size: g.size,
                text: e.currentTarget.value,
              });
            }}
            id={INPUT_ID}
            onChange={(e) => {
              const new_name = e.currentTarget.value;
              setData(data.setIn([...latest_path, 'name'], new_name));
            }}
          />
        </div>
      ) : null}
      {selected_img.show ? (
        <React.Fragment>
          <div
            className={styles.imgMoveRight}
            style={{
              top: selected_img.offset.y,
              left: selected_img.offset.x,
              marginLeft: selected_img.size.width,
              marginTop: selected_img.size.height,
            }}
            onClick={(e) => {
              e.stopPropagation();
              const index = selected_img.index;
              setData(
                data.updateIn([...latest_path, 'imgs'], (list) => {
                  const item = list.get(index);
                  return list.deleteIn([index]).insert(index + 1, item);
                }),
              );
              setSelectedImg({ ...selected_img, show: false });
            }}
            {...img_stop_propagation_events}
          >
            →
          </div>
          <div
            className={styles.imgMoveLeft}
            style={{
              top: selected_img.offset.y,
              left: selected_img.offset.x,
              marginTop: selected_img.size.height,
            }}
            onClick={(e) => {
              e.stopPropagation();
              const index = selected_img.index;
              setData(
                data.updateIn([...latest_path, 'imgs'], (list) => {
                  const item = list.get(index);
                  return list
                    .deleteIn([index])
                    .insert(Math.max(0, index - 1), item);
                }),
              );
              setSelectedImg({ ...selected_img, show: false });
            }}
            {...img_stop_propagation_events}
          >
            ←
          </div>
          <div
            className={styles.imgRemove}
            style={{
              top: selected_img.offset.y,
              left: selected_img.offset.x,
              marginLeft: selected_img.size.width,
            }}
            onClick={(e) => {
              e.stopPropagation();
              const index = selected_img.index;
              setData(
                data.updateIn([...latest_path, 'imgs'], (list) =>
                  list.deleteIn([index]),
                ),
              );
              setSelectedImg({ ...selected_img, show: false });
            }}
            {...img_stop_propagation_events}
          >
            ✕
          </div>
          <input
            style={{
              top: selected_img.offset.y + selected_img.size.height,
              left: selected_img.offset.x,
            }}
            type="checkbox"
            className={styles.independent}
            checked={selected_img.independent}
            onChange={(e) => {
              setData(
                data.setIn(
                  [...latest_path, 'imgs', selected_img.index, 'independent'],
                  e.target.checked,
                ),
              );
              setSelectedImg({
                ...selected_img,
                independent: e.target.checked,
              });
            }}
            onClick={(e) => e.stopPropagation()}
            {...img_stop_propagation_events}
          />
          <div
            style={{
              top: selected_img.offset.y,
              left: selected_img.offset.x,
            }}
            {...img_stop_propagation_events}
            className={styles.btnEdit}
            onClick={(e) => {
              e.stopPropagation();
              const img = data.getIn([
                ...latest_path,
                'imgs',
                selected_img.index,
                'src',
              ]);
              setCanvasEditor({
                show: true,
                img,
                cb: (svg, size) => {
                  const p = data
                    .getIn([...latest_path, 'imgs', selected_img.index])
                    .toJS();
                  setData(
                    data.setIn(
                      [...latest_path, 'imgs', selected_img.index],
                      fromJS({
                        ...p,
                        src: svgToDataURL(svg),
                        size,
                      }),
                    ),
                  );
                  setCanvasEditor({ show: false });
                  unselect();
                },
              });
            }}
          >
            ✎
          </div>
        </React.Fragment>
      ) : null}
      <div
        className={styles.help}
        style={
          help_clicked
            ? {
                animation: 'none',
              }
            : {}
        }
        onClick={(e) => {
          e.stopPropagation();
          window.open(
            'https://gitee.com/mind-slide/mind-slide/wikis/MindSlide%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E',
            // 'https://www.yuque.com/u1952761/zs833g',
          );
          set_help_clicked(true);
        }}
      >
        ?
      </div>
      <div
        className={styles.zoomOut}
        onClick={(e) => {
          e.stopPropagation();
          if (get_latest_selected()) {
            unselect();
          }
          const new_scale = Math.max(scale - 0.1, 0.2);
          set_scale(new_scale);
          setScale(new_scale);
        }}
      >
        -
      </div>
      <div
        className={styles.zoomIn}
        onClick={(e) => {
          e.stopPropagation();
          if (get_latest_selected()) {
            unselect();
          }
          const new_scale = scale + 0.1;
          set_scale(new_scale);
          setScale(new_scale);
        }}
      >
        +
      </div>
      {canvasEditor.show ? (
        <React.Suspense fallback={<div>...</div>}>
          <CanvasEditor
            img={canvasEditor.img}
            slideWidth={slideWidth}
            onCommit={(e, size) => {
              canvasEditor.cb(e, size);
            }}
            onClose={() => setCanvasEditor({ show: false })}
          />
        </React.Suspense>
      ) : null}
      {sharingDialog.show ? (
        <div className={styles.sharingDialog}>
          <div
            className={styles.btn}
            onClick={() => {
              setSharingDialog({
                show: false,
              });
            }}
          >
            关闭
          </div>
          <div>
            上传状态：
            {sharingDialog.current === sharingDialog.total
              ? '已完成'
              : '上传中'}
          </div>
          <div>
            已上传：{sharingDialog.current}/{sharingDialog.total}
          </div>
          <div>
            共享节点地址：
            <span style={{ userSelect: 'all' }}>{sharingDialog.hash}</span>
          </div>
          <div>
            共享节点网址：
            <span style={{ userSelect: 'all' }}>
              https://mindslide.cn/#{sharingDialog.hash}
            </span>
          </div>
          <div>三种使用方法：</div>
          <div>(a) 使用共享节点地址插入节点</div>
          <div>(b) 使用共享节点地址导入项目</div>
          <div>(c) 使用共享节点网址</div>
        </div>
      ) : null}
    </div>
  );
};
